package br3akr.DeadlyDinnerParty.Server.Actions;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

import br3akr.DeadlyDinnerParty.Server.Engine.Session;
import br3akr.DeadlyDinnerParty.Server.Exceptions.DeadlyDinnerPartyException;
import br3akr.DeadlyDinnerParty.Server.World.Area;
import br3akr.DeadlyDinnerParty.Server.World.Chest;
import br3akr.DeadlyDinnerParty.Server.World.Exit;
import br3akr.DeadlyDinnerParty.Server.World.GameCharacter;
import br3akr.DeadlyDinnerParty.Server.World.Item;
import br3akr.DeadlyDinnerParty.Server.World.Key;
import br3akr.DeadlyDinnerParty.Server.World.Exit.ExitStatus;

public class OpenCloseAction extends Action {
	boolean actionComplete;
    private ActionResponse actionResponse;
    public static final Set<String> keywords;
	static
	{
		Set<String>tempKeywords = new HashSet<String>();
		tempKeywords.add("Open");
		tempKeywords.add("Op");
		tempKeywords.add("Close");
		tempKeywords.add("Cl");
		tempKeywords.add("Lock");
		tempKeywords.add("Lo");
		tempKeywords.add("Un");
		tempKeywords.add("Unlock");
		keywords = Collections.unmodifiableSet(tempKeywords);
	}
    
    public OpenCloseAction() {
        super("OpenClose", false);
    }
    
    public ActionResponse doAction(String action, Session session) throws DeadlyDinnerPartyException {
		if (action == null) {
			throw new DeadlyDinnerPartyException ("No action found.");
		}
		if (session == null) {
			throw new DeadlyDinnerPartyException ("No session found.");
		}
		
    	StringTokenizer tokenizedAction = new StringTokenizer(action);
    	String keyword = tokenizedAction.nextToken();
    	String param1 = null;
    	
    	if (tokenizedAction.hasMoreTokens()) {
    		param1 = tokenizedAction.nextToken();
    	}
    	
		GameCharacter actor = session.getCharacter();
		
		if (actor == null) {
			throw new DeadlyDinnerPartyException ("No character found for your session.");
		}
    	
    	actionResponse = new ActionResponse();
    	actionResponse.setActingCharacter(session.getCharacter());
    	Area location = session.getCharacter().getLocation();
    	Set<GameCharacter> localOnlookers = location.getAreaOnlookers(session.getCharacter(), null);
    	Set<GameCharacter> exitOnlookers = null;
    	Area exitLocation = null;
    	Exit exit = null;
    	Chest chest = null;
    	ExitStatus curExitStatus = null;
    	Exit.ExitDirections exitDir = null;
    	
		switch (keyword.toLowerCase()) {
			case "open":
			case "op":
				if (actor.isRecovering()) {
					throw new DeadlyDinnerPartyException ("You can not open doors while recovering.");
				}
				
				if (actor.getStatus() == GameCharacter.Status.INCAPACITATED) {
					throw new DeadlyDinnerPartyException ("You can not open doors while incapacitated.");
				}
				
				exit = location.getExit(Exit.getDirection(param1));
		    	
		    	if (exit == null) {
		    		throw new DeadlyDinnerPartyException (param1 + " is an invalid exit.");
		    	}
		    	
		    	exitLocation = exit.getExitArea(location);
		    	
		    	exitOnlookers = exitLocation.getAreaOnlookers(session.getCharacter(), null);
		    	actionResponse.addAffectedArea(location);
		    	actionResponse.addAffectedArea(exitLocation);
		    	exitDir = exit.getExitDirection(location);
				
				if (exit.getStatus() == Exit.ExitStatus.LOCKED) {
					actionResponse.addOnlookers(localOnlookers, session.getCharacter().getName() + " jiggles the handle of the door " + exit + " to " + exitLocation.getName() + ".");
					actionResponse.addOnlookers(exitOnlookers, "The handle of the door " + exit.getExitDirection(exitLocation) + " to " + location.getName() + " rattles. Someone is trying to open it.");
					actionResponse.setActingCharacterDescription("You try to open the door " + exitDir + " to " + exitLocation.getName() + ", but it's locked.");
					beginRecovery(session.getCharacter(), 1000);
				} else {
					curExitStatus = exit.getStatus();
					
					if (curExitStatus == ExitStatus.OPEN) {
						throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is already opened.");
					}
					if (curExitStatus == ExitStatus.BASHED) {
						throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is bashed open and cannot be opened.");
					}
					if (curExitStatus == ExitStatus.CLOSED) {
						exit.setStatus(ExitStatus.OPEN);
					}
					
					actionResponse.addOnlookers(localOnlookers, session.getCharacter().getName() + " opens the door to " + exitLocation.getName() + ".");
					actionResponse.addOnlookers(exitOnlookers, "Someone opens the door to the " + location.getName() + ".");
					actionResponse.setActingCharacterDescription("You open the door to the " + exitLocation.getName() + ".");
					beginRecovery(session.getCharacter(), 3000);
				}
				break;
			case "close":
			case "cl":
				if (actor.isRecovering()) {
					throw new DeadlyDinnerPartyException ("You can not close doors while recovering.");
				}
				
				if (actor.getStatus() == GameCharacter.Status.INCAPACITATED) {
					throw new DeadlyDinnerPartyException ("You can not close doors while incapacitated.");
				}
				
				exit = location.getExit(Exit.getDirection(param1));
		    	
		    	if (exit == null) {
		    		throw new DeadlyDinnerPartyException (param1 + " is an invalid exit.");
		    	}
		    	
		    	exitLocation = exit.getExitArea(location);
		    	
		    	exitOnlookers = exitLocation.getAreaOnlookers(session.getCharacter(), null);
		    	actionResponse.addAffectedArea(location);
		    	actionResponse.addAffectedArea(exitLocation);
				
				curExitStatus = exit.getStatus();
				exitDir = exit.getExitDirection(location);
				if (curExitStatus == null) {
					throw new DeadlyDinnerPartyException("There is no door to the " + exitDir + " to close.");
				}
				if (curExitStatus == ExitStatus.CLOSED || curExitStatus == ExitStatus.LOCKED) {
					throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is already closed.");
				}
				if (curExitStatus == ExitStatus.BASHED) {
					throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is bashed open and cannot be closed.");
				}
				if (curExitStatus == ExitStatus.OPEN) {
					exit.setStatus(ExitStatus.CLOSED);
				} else {
					throw new DeadlyDinnerPartyException("The status of the door is unknown.");
				}
				
				actionResponse.addOnlookers(localOnlookers, session.getCharacter().getName() + " closes the door to the " + exitLocation.getName() + ".");
				actionResponse.addOnlookers(exitOnlookers, "Someone closes the door to the " + location.getName() + ".");
				actionResponse.setActingCharacterDescription("You close the door to the " + exitLocation.getName() + ".");
				beginRecovery(session.getCharacter(), 1000);
				break;
			case "lock":
			case "lo":
				if (actor.isRecovering()) {
					throw new DeadlyDinnerPartyException ("You can not lock doors while recovering.");
				}
				
				if (actor.getStatus() == GameCharacter.Status.INCAPACITATED) {
					throw new DeadlyDinnerPartyException ("You can not lock doors while incapacitated.");
				}
				
				exit = location.getExit(Exit.getDirection(param1));
		    	
		    	if (exit == null) {
		    		chest = location.getChest(param1);
		    		
		    		if (chest == null) {
		    			throw new DeadlyDinnerPartyException (param1 + " must either be the name of a chest, or an exit.");
		    		}
		    	}
		    	
		    	if (exit != null) {
			    	exitLocation = exit.getExitArea(location);
			    	
			    	if (exitLocation == null) {
			    		throw new DeadlyDinnerPartyException ("There is no exit " + exit);
			    	}
			    	
			    	exitOnlookers = exitLocation.getAreaOnlookers(session.getCharacter(), null);
			    	actionResponse.addAffectedArea(location);
			    	actionResponse.addAffectedArea(exitLocation);
			    	exitDir = exit.getExitDirection(location);
			    	
					if (hasKeyForDoor(actor, exit)) {
						curExitStatus = exit.getStatus();
						if (curExitStatus == null) {
							throw new DeadlyDinnerPartyException("There is no door to the " + exitDir + " to lock.");
						}
						if (curExitStatus == ExitStatus.OPEN) {
							throw new DeadlyDinnerPartyException("The door to the " + exitDir + " must be closed before being locked.");
						}
						if (curExitStatus == ExitStatus.BASHED) {
							throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is bashed open, and cannot be closed or locked.");
						}
						if (curExitStatus == ExitStatus.LOCKED) {
							throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is already locked.");
						}
						if (curExitStatus == ExitStatus.CLOSED) {
							exit.setStatus(ExitStatus.LOCKED);
						} else {
							throw new DeadlyDinnerPartyException("The status of the door is unknown.");
						}
						actionResponse.addOnlookers(localOnlookers, session.getCharacter().getName() + " locks the door to the " + exitLocation.getName() + ".");
						actionResponse.addOnlookers(exitOnlookers, "Someone locks the door to the " + location.getName() + ".");
						actionResponse.setActingCharacterDescription("You lock the door to the " + exitLocation.getName() + ".");
					} else {
						actionResponse.setActingCharacterDescription("You have no key for the door to " + exitLocation.getName() + ".");
					}
					beginRecovery(session.getCharacter(), 3000);
		    	} else {
			    	actionResponse.addAffectedArea(location);

					if (hasKeyForChest(actor, chest)) {
						chest.setLocked(true);
						actionResponse.addOnlookers(localOnlookers, session.getCharacter().getName() + " locks " + chest.getName() + ".");
						actionResponse.setActingCharacterDescription("You lock " + chest.getName() + ".");
					} else {
						actionResponse.setActingCharacterDescription("You have no key for that chest.");
					}
					beginRecovery(session.getCharacter(), 3000);
		    	}
				break;
			case "unlock":
			case "un":
				if (actor.isRecovering()) {
					throw new DeadlyDinnerPartyException ("You can not lock doors while recovering.");
				}
				
				if (actor.getStatus() == GameCharacter.Status.INCAPACITATED) {
					throw new DeadlyDinnerPartyException ("You can not lock doors while incapacitated.");
				}
				
				exit = location.getExit(Exit.getDirection(param1));
		    	
		    	if (exit == null) {
		    		throw new DeadlyDinnerPartyException ("There is no exit " + param1 + " to unlock.");
		    	}
		    	
		    	exitLocation = exit.getExitArea(location);
		    	
		    	if (exitLocation == null) {
		    		throw new DeadlyDinnerPartyException ("There is no exit " + exit);
		    	}
		    	
		    	exitOnlookers = exitLocation.getAreaOnlookers(session.getCharacter(), null);
		    	actionResponse.addAffectedArea(location);
		    	actionResponse.addAffectedArea(exitLocation);
		    	exitDir = exit.getExitDirection(location);
		    	
				if (hasKeyForDoor(actor, exit)) {
					curExitStatus = exit.getStatus();
					if (curExitStatus == null) {
						throw new DeadlyDinnerPartyException("There is no door to the " + exitDir + " to unlock.");
					}
					if (curExitStatus == ExitStatus.OPEN || curExitStatus == ExitStatus.CLOSED) {
						throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is already unlocked.");
					}
					if (curExitStatus == ExitStatus.BASHED) {
						throw new DeadlyDinnerPartyException("The door to the " + exitDir + " is already bashed open.");
					}
					if (curExitStatus == ExitStatus.LOCKED) {
						exit.setStatus(ExitStatus.CLOSED);
					} else {
						throw new DeadlyDinnerPartyException("The status of the door is unknown.");
					}
					actionResponse.addOnlookers(localOnlookers, session.getCharacter().getName() + " locks the door to the " + exitLocation.getName() + ".");
					actionResponse.addOnlookers(exitOnlookers, "Someone locks the door to the " + location.getName() + ".");
					actionResponse.setActingCharacterDescription("You unlock the door to the " + exitLocation.getName() + ".");
				} else {
					actionResponse.setActingCharacterDescription("You have no key for the door to " + exitLocation.getName() + ".");
				}
				beginRecovery(session.getCharacter(), 3000);
				break;
			default:
				break;
		}
        
    	actionComplete = true;
        return actionResponse;
    }
    
    private boolean hasKeyForDoor(GameCharacter c, Exit exit) {
    	boolean retval = false;
    	Key key = null;
		for (Item i : c.getInventory()) {
			if (i instanceof Key) {
				key = (Key)i;
				if (key.keyForDoorLock(exit)) {
					retval = true;
				}
			}
		}
		if (c.getHeldItem() instanceof Key) {
			key = (Key)c.getHeldItem();
			if (key.keyForDoorLock(exit)) {
				retval = true;
			}
		}
    	return retval;
    }
    
    private boolean hasKeyForChest(GameCharacter c, Chest chest) {
    	boolean retval = false;
    	Key key = null;
		for (Item i : c.getInventory()) {
			if (i instanceof Key) {
				key = (Key)i;
				if (key.keyForChestLock(chest)) {
					retval = true;
				}
			}
		}
		if (c.getHeldItem() instanceof Key) {
			key = (Key)c.getHeldItem();
			if (key.keyForChestLock(chest)) {
				retval = true;
			}
		}
		return retval;
    }
	
    public ActionResponse getActionResponse() {
		return actionResponse;
	}
    
    public Set<String> getKeywords() {
        return keywords;
    }
}